NTQQ (macOS ARM,无需关闭 SIP)
教程验证状态
当前状态: 尚未确认本教程在近期版本上仍可用
已确认 QQ 版本: 暂无
最后确认时间: 暂无
如果你确认本教程在 macOS ARM 平台上依然可用,请打开 PR 或 issue 通知维护者,并注明 QQ 版本、macOS 版本、架构,以及是否成功提取原始数据库和 key。
测试环境:QQ NT 6.9.96,macOS 26 Sequoia / macOS 27 Tahoe(Apple Silicon,arm64)
与原 ARM 教程的区别:原教程要求关闭 SIP,在 macOS 14+ 上关闭 SIP 操作繁琐,macOS 27 早期开发者测试版更存在
csrutil disable无效的已知 bug。本教程无需关闭 SIP,也无需 Hopper 等付费反汇编工具,全程使用系统自带工具和 Python 标准库。
0. 原理
QQ NT 的 wrapper.node(一个 Electron native module)内部静态链接了 SQLCipher,并通过内部函数 nt_sqlite3_key_v2 向 SQLite 数据库传递密钥。该函数在 QQ 首次打开数据库时(即点击登录后)被调用,此时密钥以明文形式存在于 x2 寄存器中。
lldb 默认无法 attach 到带有 hardened runtime + library validation 的 QQ 进程。解决方案:用 ad-hoc 签名重新签名 QQ,绕过该限制,无需关闭 SIP。
1. 准备工作
只需系统自带工具:
- lldb(Xcode Command Line Tools 自带)
- Python 3(macOS 自带)
- 本页只需用于提取 key;SQLCipher 解密和数据库转换统一在 Windows 上完成
2. Ad-hoc 重新签名 QQ(绕过 lldb attach 限制)
# 移除原签名,用 ad-hoc 签名替代(不需要开发者证书)
sudo codesign --remove-signature /Applications/QQ.app
sudo codesign --force --deep --sign - /Applications/QQ.app说明:
-s -表示 ad-hoc 签名(系统生成的伪签名)。QQ 大部分功能正常,可能偶尔提示"QQ 已被修改",忽略即可。重新签名信息存储在二进制文件里,Mac 重启后无需重新签名,重装 QQ 后需要重新执行一次。
3. 下载脚本
本教程配套三个脚本,位于仓库 scripts/macos/arm-nosip/ 目录:
| 文件 | 用途 |
|---|---|
qq_web.py | 一键 GUI 工具(最推荐):自动检测签名、备份数据库、提取密钥;得到原始数据库和 key 后转移到 Windows |
find_key_func.py | 独立 VA 分析工具(命令行打印地址) |
qq_key_extractor.py | lldb 自动化模块(全自动:自动设断点、自动打印密钥);已内嵌在 qq_web.py 中,单独使用时才需要下载 |
# 只需下载 qq_web.py 即可(推荐)
curl -sO https://raw.githubusercontent.com/QQBackup/qq-win-db-key/master/scripts/macos/arm-nosip/qq_web.py
# 若需要命令行脚本,额外下载:
curl -sO https://raw.githubusercontent.com/QQBackup/qq-win-db-key/master/scripts/macos/arm-nosip/find_key_func.py
curl -sO https://raw.githubusercontent.com/QQBackup/qq-win-db-key/master/scripts/macos/arm-nosip/qq_key_extractor.py4. 提取密钥
方式 0:GUI 一键工具(最推荐)
下载 qq_web.py 后,只需一条命令:
pip3 install flask && python3 qq_web.py浏览器自动打开 http://127.0.0.1:8899,按页面步骤操作:
- 第一步 - 重新签名:若 QQ 已是 ad-hoc 签名则自动跳过;否则点击按钮,工具会先备份数据库再重新签名。
- 第二步 - 提取密钥:点击"启动 QQ + 注入 lldb",等待工具自动完成。
- 若 QQ 已保存账号自动登录(启动后直接显示聊天列表):密钥通常自动捕获,无需任何操作。
- 若 QQ 显示登录界面(断点就绪提示出现后):点击 QQ 内的「登录」按钮,密钥自动显示。
- 若 QQ 自动登录但未捕获密钥:在 QQ 主界面点击账号头像 → 选「退出账号」,返回登录界面后点「登录」。不要使用 macOS 菜单栏的「切换账号」,那会重启 QQ 进程导致断点失效。
- 第三步 - 导出数据:填写导出路径,一键导出为 HTML 聊天记录。
注意:
qq_key_extractor.py已内嵌在qq_web.py中,无需单独下载。
方式 A:命令行全自动
使用 qq_key_extractor.py,全程只需 5 条命令,密钥自动打印。
先关闭 QQ,然后打开两个终端:
终端 A(先执行):
lldb -n QQ -w --one-line "command script import ./qq_key_extractor.py"脚本加载后会自动分析 wrapper.node 并打印使用说明。
终端 B:
open /Applications/QQ.app回到终端 A,按提示操作:
# lldb 已 attach,依次输入:
(lldb) process continue ← 放行,等 QQ 出现登录界面
[等 QQ 出现登录界面,按 Ctrl-C 暂停]
(lldb) qq-setbp ← 自动找 slide,自动设断点
# 输出:wrapper.node 加载地址: 0x... 断点已设置
(lldb) c ← 继续运行然后在 QQ 界面点击登录(或点击任意聊天消息),密钥自动打印:
==============================================================
[+] 密钥提取成功!
KEY : <你的密钥会显示在这里>
LENGTH : 16 bytes
==============================================================QQ 会自动继续运行,无需手动输入 c。
方式 B:手动(仅需 find_key_func.py)
先用脚本获取函数 VA:
python3 find_key_func.py
# 输出示例:
# [+] nt_sqlite3_key_v2 VA: 0x32e74f8然后用 lldb 手动操作:
终端 A:
lldb -n QQ -w终端 B:
open /Applications/QQ.app在终端 A 的 lldb 里:
(lldb) process continue
[等 QQ 出现登录界面,按 Ctrl-C]
(lldb) image list -o -f | grep wrapper.node
# 示例:[ 0] 0x0000000130190000 /Applications/QQ.app/...
(lldb) expr (unsigned long)0x130190000 + 0x32e74f8
# 填入你自己的 slide 和 VA
(lldb) br s -a <上步结果>
(lldb) c在 QQ 点击登录,断点命中后:
(lldb) register read x2 x3
(lldb) memory read --format c --count <x3的值> --size 1 <x2的值>
# 示例输出:<你的密钥>
(lldb) cx3 = 密钥实际长度(通常为 16),以 x3 为准,不要硬编码 16。
5. 找到数据库文件
新版 QQ NT(6.9.x)的数据库路径(注意:不在旧版的 ~/Library/Containers/ 下):
ls ~/Library/Application\ Support/QQ/
# 找到 nt_qq_<MD5> 目录
ls ~/Library/Application\ Support/QQ/nt_qq_*/nt_db/
# nt_msg.db ← 主消息数据库(约 70MB)
# group_info.db备份数据库(禁止用 sudo cp,会导致复制出空文件):
mkdir -p ~/qq-extract
cp ~/Library/Application\ Support/QQ/nt_qq_*/nt_db/nt_msg.db ~/qq-extract/6. 转移到 Windows
本页到这里完成 macOS 端的原始数据库与 key 提取。请将 ~/qq-extract/nt_msg.db 和上一步得到的 key 一起复制到 Windows,不要在 macOS 上直接解密或覆盖原文件;后续按统一解密处理。
7. 已知问题 & 注意事项
| 问题 | 原因 | 解决 |
|---|---|---|
sudo cp 沙盒数据库 → 复制出空文件 | 沙盒 ACL 保护,root 也无法绕过 | 用普通 cp(当前用户有权限) |
| QQ 报"被修改" | ad-hoc 签名替换了原签名 | 忽略,不影响使用 |
| 断点没有命中 | QQ 已自动登录且密钥已传递 | 在 QQ 内点头像 → 「退出账号」后重新登录(不要关闭 QQ) |
| 菜单栏「切换账号」导致断点失效 | 该选项会重启 QQ 进程,原断点消失 | 改用 QQ 应用内的「退出账号」,保持进程不变 |
image list 找不到 wrapper.node | 进程太早被暂停,模块未加载 | process continue 等几秒再 Ctrl-C |
| 解密参数报错 | 应在 Windows 统一解密页中按脚本 1 处理 | 参阅统一解密 |
| 密钥长度不是 16 | 版本差异,以 x3 寄存器值为准 | --count <x3值> |
致谢
本教程基于 lengyue 的原始 ARM 教程 改进,主要贡献:
- 无需关闭 SIP:用 ad-hoc 重新签名代替禁用 SIP,兼容 macOS 27 Tahoe
- 无需 Hopper:Python 脚本自动定位函数 VA
- 更新数据库路径:修正 QQ 6.9.x 的新沙盒路径
- 动态读取密钥长度:从 x3 寄存器读取实际长度,不硬编码 16 字节
- 测试版本:QQ NT 6.9.96,macOS 27 Tahoe Developer Beta 1(arm64)